#include "SimpleIDLHandler.hpp"
#include <boost/foreach.hpp>

using namespace dsg;

namespace sidl
{
    UniFileItr file_at(size_t index)
    {
        return UniDataModel::getInstance()->getWorkspace().beginFiles() + index;
    }

    UniEntityItr entity_at(size_t file_index, size_t entity_index)
    {
        return file_at(file_index)->beginEntities() + entity_index;
    }

    UniAttributeItr attribute_at(size_t file_index, size_t entity_index, size_t attribute)
    {
        return entity_at(file_index, entity_index)->beginAttributes() + attribute;
    }

    UniString format_comment(vector<UniString> const& v)
    {
        UniString comments;
        BOOST_FOREACH(UniString const& str, v)
        {
            comments += str + L"\r\n";
        }
        return comments.size()>=2?
                comments.erase(comments.size()-2):
                comments;
    }

    type_handler::type_handler(size_t file, size_t entity, size_t attribute)
    : m_file(file), 
      m_entity(entity),
      m_attribute(attribute)
    {}

    void type_handler::operator()(dsg::type_t type)
    {
        attribute_at(m_file, m_entity, m_attribute)->setType(type);
    }

    void type_handler::operator()(UniString const& type)
    {
        UniFileItr      file      = file_at(m_file);
        UniEntityItr    entity    = entity_at(m_file, m_entity);
        UniAttributeItr attribute = attribute_at(m_file, m_entity, m_attribute);

        attribute->setType(ATTR_UINT64);
        attribute->setModifier(ATTR_REF);
        file->addNewRelation(L"R_" + entity->getName() + L"_" + type, 
                             entity->getName(), 
                             attribute->getName(),
                             type,
                             L"Id");
    }

    member_handler::member_handler(size_t file, size_t entity)
    : m_file(file), 
      m_entity(entity)
    {}

    void member_handler::operator()(idl_attribute const& attr)
    {
        UniEntityItr entity;
        UniFileItr file = file_at(m_file);
        if (!attr.index)
        {
            entity = entity_at(m_file, m_entity);
        }
        else
        {
            UniEntity  source = *entity_at(m_file, m_entity);
            entity = file->addNewEntity(source.getName() + L"_" + attr.name, attr.name, ENTITY_DATA, format_comment(attr.comments));
            entity->addNewAttribute(L"Id", *(attr.index), ATTR_ID | ATTR_CONST);
            entity->addNewAttribute(source.getName(), ATTR_UINT64, ATTR_REF | ATTR_CONTEXTUAL);
            file->addNewRelation(L"R_" + entity->getName() + L"_" + source.getName(), 
                                 entity->getName(), 
                                 source.getName(),
                                 source.getName(),
                                 L"Id");
        }
        UniAttributeItr attribute = entity->addNewAttribute(attr.name);
        attribute->setComment(format_comment(attr.comments));
        BOOST_FOREACH(attribute_modifier_t const& modifier, attr.modifiers)
        {
            switch (modifier)
            {
            case IDL_ATTR_READONLY :
                attribute->setModifier(ATTR_CONST);
                break;
            case IDL_ATTR_STATIC :
                attribute->setModifier(ATTR_STATIC);
                break;
            default: break;
            }
        }
        if (attr.value)
        {
            attribute->setDefault(*(attr.value));
        }
        type_handler handler(m_file, entity - file->beginEntities(), attribute - entity->beginAttributes());
        boost::apply_visitor(handler, attr.type);  
    }

    void member_handler::operator()(idl_event const& ev)
    {
        (*this)(ev.prototype, format_comment(ev.comments), ATTR_VIRTUAL);
    }

    void member_handler::operator()(idl_method const& meth)
    {
        (*this)(meth.prototype, format_comment(meth.comments));
    }

    void member_handler::operator()(idl_prototype const& proto, dsg::UniString const& comments, dsg::modifier_t modifier)
    {
        UniEntity  source = *entity_at(m_file, m_entity);
        UniFileItr file   = file_at(m_file);
        UniEntityItr ent  = file->addNewEntity(source.getName() + L"_" + proto.name, proto.name, ENTITY_FUNCTION, comments);
        ent->addNewAttribute(L"Id", ATTR_UINT64, ATTR_ID | ATTR_CONST);
        // this
        ent->addNewAttribute(source.getName(), ATTR_UINT64, ATTR_REF | ATTR_CONTEXTUAL | modifier);
        UniAttributeItr att;
        // return
        if ( const idl_type* type = boost::get<const idl_type>( &(proto.return_type) ) )
        {
            att = ent->addNewAttribute(proto.name);
            att->setModifier(ATTR_AUTO);
            type_handler handler(m_file, ent - file->beginEntities(), att - ent->beginAttributes());
            boost::apply_visitor(handler, *type);
        }
        // Parameter
        BOOST_FOREACH(idl_parameter const& parameter, proto.parameters)
        {
            att = ent->addNewAttribute(parameter.name);
            switch (parameter.modifier)
            {
            case IDL_ARG_IN :
                att->setModifier(ATTR_CONST);
                break;
            case IDL_ARG_INOUT :
            case IDL_ARG_OUT :
            default:
                break;
            break;
            }
            type_handler handler(m_file, ent - file->beginEntities(), att - ent->beginAttributes());
            boost::apply_visitor(handler, parameter.type);
        }
        file->addNewRelation(L"R_" + ent->getName() + L"_" + source.getName(), 
                             ent->getName(), 
                             source.getName(),
                             source.getName(),
                             L"Id");
    }

    void struct_handler::operator()(idl_interface const& inter)
    {
        UniWorkspace* wrk = &UniDataModel::getInstance()->getWorkspace();
        UniFileItr file   = wrk->openFile(inter.name, false);
        UniEntityItr ent  = file->addNewEntity(inter.name, L"", ENTITY_DATA, format_comment(inter.comments));
        ent->addNewAttribute(L"Id", ATTR_UINT64, ATTR_ID | ATTR_CONST);

        if (inter.base)
        {
            ent->addNewAttribute(L"Base", ATTR_UINT64, ATTR_REF | ATTR_BASE);
            file->addNewRelation(L"R_" + ent->getName() + L"_" + *(inter.base), 
                                 ent->getName(), 
                                 L"Base",
                                 *(inter.base),
                                 L"Id");
        }

        member_handler handler(file - wrk->beginFiles(), ent - file->beginEntities());
        BOOST_FOREACH(idl_member const& member, inter.members)
        {
            boost::apply_visitor(handler, member);
        }
    }

    void struct_handler::operator()(idl_enum const& en)
    {
        UniWorkspace* wrk = &UniDataModel::getInstance()->getWorkspace();
        UniFileItr file   = wrk->openFile(en.name, false);
        UniEntityItr ent  = file->addNewEntity(en.name, L"", ENTITY_ENUMERATION, format_comment(en.comments));
        ent->addNewAttribute(L"Id", ATTR_UINT64, ATTR_ID | ATTR_CONST);

        BOOST_FOREACH(idl_keyword const& keyword, en.keywords)
        {
            UniAttributeItr att = ent->addNewAttribute(keyword.name);
            att->setComment(format_comment(keyword.comments));
            att->setModifier(ATTR_STATIC | ATTR_CONST);
            att->setType(ATTR_UINT64);
            if (keyword.value)
            {
                att->setDefault(*(keyword.value));
            }
        }
    }

    void file_handler::operator()(idl_file const& file)
    {
        struct_handler handler;
        BOOST_FOREACH(idl_struct const& struc, file)
        {
            boost::apply_visitor(handler, struc);
        }
    }

}